#include "stdafx.h"
//#include "uFunction.h"
#include "xDocument.h"
#include "xElement.h"
#include "xAttribute.h"
#include "xContentNode.h"
#include "xTextNode.h"

#include "assert.h"
#include "io.h"

#include <fstream>

namespace ck
{
	namespace xml
	{
		void on_libxml_construct(xmlNode* node);
		void on_libxml_destruct(xmlNode* node);

		void on_libxml_construct(xmlNode* node)
		{
			switch (node->type)
			{
			case XML_ELEMENT_NODE:
			{
				node->_private = new Element(node);
				break;
			}
			case XML_ATTRIBUTE_NODE:
			{
				node->_private = new Attribute(node);
				break;
			}
			case XML_TEXT_NODE:
			{
				node->_private = new TextNode(node);
				break;
			}
			//case XML_COMMENT_NODE:
			//{
			//	node->_private = new XCommentNode(node);
			//	break;
			//}
			//case XML_CDATA_SECTION_NODE:
			//{
			//	node->_private = new XCdataNode(node);
			//	break;
			//}
			//case XML_PI_NODE:
			//{
			//	node->_private = new XProcessingInstructionNode(node);
			//	break;
			//}
			//case XML_DTD_NODE:
			//{
			//	node->_private = new XDtd(reinterpret_cast<xmlDtd*>(node));
			//	break;
			//}
			//case XML_ENTITY_NODE:
			//{
			//  assert(0 && "Warning: XML_ENTITY_NODE not implemented");
			//  //node->_private = new xmlpp::ProcessingInstructionNode(node);
			//  break;
			//}
			//case XML_ENTITY_REF_NODE:
			//{
			//	node->_private = new XEntityReference(node);
			//	break;
			//}
			case XML_DOCUMENT_NODE:
			{
				// do nothing ! in case of documents it's the wrapper that is the owner
				break;
			}
			default:
			{
				// good default for release versions
				node->_private = new Node(node);
				assert(0 && "Warning: new node of unknown type created");
				break;
			}
			}
		}

		//Called by libxml whenever it destroys something
		//such as a node or attribute.
		//This allows us to delete the C++ instance for the C instance, if any.
		void on_libxml_destruct(xmlNode* node)
		{
			bool bPrivateDeleted = false;
			if (node->type == XML_DTD_NODE)
			{
				//XDtd* cppDtd = static_cast<XDtd*>(node->_private);
				//if (cppDtd)
				//{
				//	delete cppDtd;
				//	bPrivateDeleted = true;
				//}
			}
			else if (node->type == XML_DOCUMENT_NODE)
			{
				// do nothing. See on_libxml_construct for an explanation
			}
			else
			{
				Node* cppNode = static_cast<Node*>(node->_private);
				if (cppNode)
				{
					delete cppNode;
					bPrivateDeleted = true;
				}
			}

			//This probably isn't necessary:
			if (bPrivateDeleted)
				node->_private = 0;
		}

		Document::Init::Init()
		{
			xmlInitParser(); //Not always necessary, but necessary for thread safety.
			xmlRegisterNodeDefault(on_libxml_construct);
			xmlDeregisterNodeDefault(on_libxml_destruct);
			xmlThrDefRegisterNodeDefault(on_libxml_construct);
			xmlThrDefDeregisterNodeDefault(on_libxml_destruct);
		}

		Document::Init::~Init()
		{
			xmlCleanupParser(); //As per xmlInitParser(), or memory leak will happen.
		}

		Document::Init Document::init_;

		Document::Document(const char* version/*="1.0"*/)
		{
			m_pxDoc = xmlNewDoc((const xmlChar*)version);
			m_pxDoc->_private = this;
			m_pBuffer = NULL;
		}


		Document::~Document()
		{
			if (m_pBuffer != NULL)
			{
				xmlFree(m_pBuffer);
				m_pBuffer = NULL;
			}
			if (m_pxDoc != NULL)
			{
				xmlFreeDoc(m_pxDoc);
				m_pxDoc = NULL;
			}
		}

		Element* Document::CreateRootNode(const char* szName)
		{
			if (szName == NULL)
			{
				return NULL;
			}

			//const char* szNameUtf8 = auge_encoding_convert("GBK", "UTF-8", szName, strlen(szName));
			//xmlNodePtr pxmlRootNode = xmlNewDocNode(m_pxDoc, 0, (const xmlChar*)szNameUtf8, 0);
			xmlNodePtr pxmlRootNode = xmlNewDocNode(m_pxDoc, 0, (const xmlChar*)szName, 0);
			if (pxmlRootNode == NULL)
			{
				return NULL;
			}
			xmlDocSetRootElement(m_pxDoc, pxmlRootNode);

			return GetRootNode();
		}

		Element* Document::GetRootNode()
		{
			xmlNode* root = xmlDocGetRootElement(m_pxDoc);
			if (root == NULL)
			{
				return NULL;
			}	

			return reinterpret_cast<Element*>(root->_private);
		}

		z_int32 Document::Load(const char* path)
		{
			if (!path)
			{
				return -1;
			}
			if (_access(path, 0) == -1)
			{
				return -1;
			}
			if (m_pxDoc)
			{
				xmlFreeDoc(m_pxDoc);
				m_pxDoc = NULL;
			}
			m_pxDoc = xmlParseFile(path);
			return m_pxDoc ? 0 : -1;
		}

		z_int32 Document::Save(const char* path, const char* encoding/*= U_ENCODING_GBK*/, z_int32 format/*= 0*/)
		{
			//char* pmem = NULL;
			//int size = 0;
			////xmlDocDumpMemory(m_pxDoc, (xmlChar**)&pmem, &size);
			//xmlDocDumpFormatMemoryEnc(m_pxDoc, (xmlChar**)&pmem, &size, encoding, format);
			//TRACE1("%s\n", pmem);
			//xmlFree(pmem);

			//std::ofstream os(path);
			//os.write(pmem, size);
			//os.close();

			int ret = xmlSaveFormatFileEnc(path, m_pxDoc, encoding, format);
			//int ret = xmlSaveFileEnc(path, m_pxDoc, encoding);
			
			
			return 0;
		}

		void Document::Close()
		{
			if (m_pBuffer != NULL)
			{
				xmlFree(m_pBuffer);
				m_pBuffer = NULL;
			}
			if (m_pxDoc != NULL)
			{
				xmlFreeDoc(m_pxDoc);
				m_pxDoc = NULL;
			}
		}

		void Document::SetKeepBlanks(bool flag)
		{
			xmlKeepBlanksDefault(flag ? 1 : 0);
		}
	}
}